package com.cht.commonUtil.quartz;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNLogClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import com.cht.commonUtil.enums.CheckInStatus;
import com.cht.commonUtil.structure.I1S1;
import com.cht.commonUtil.widgets.CommitMediator;
import com.cht.commonUtil.widgets.Widgets;

public class QuartzChkinJob extends Thread implements Job {

	List<I1S1> fileList = new ArrayList<I1S1>();
	
	public void execute(JobExecutionContext ctx) throws JobExecutionException {
		
		/*
		 *	簽入檔案 
		 * 
		 * */
		
		
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		//SVN抓取檔案要用
		InputStream ins = null;
		ByteArrayOutputStream baos = null;
		SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
		SVNClientManager manager = null;
		SVNRepository cmRepo = null; // SVN CM OL的Repo
		SVNRepository apRepo = null; // SVN AP 的repo
		
		try {
			System.out.println("開始執行了");
			conn = Widgets.getConn();
			
			//取得資訊
			JobDetail jd = ctx.getJobDetail();
			JobDataMap jdm = jd.getJobDataMap();
			String subsysId = jdm.getString("subsysId");
			String svnUrl = jdm.getString("svnUrl");
			String svnPath = jdm.getString("svnPath");
			String empId = jdm.getString("empId");
			
			List<String> svnCM_MR = Widgets.getSVN4SubsysConnInfo(subsysId, 1);	//SVN CM MR的連線資訊
			List<String> svnAP = Widgets.getSVN4SubsysConnInfo(subsysId, 4);	//SVN AP OL的連線資訊
			apRepo = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(svnAP.get(0)));	//連線到SVN AP OL 的repo
			ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(svnAP.get(1), svnAP.get(2));
	        apRepo.setAuthenticationManager(authManager);
			long apVer = apRepo.getLatestRevision();
			
			//新增繳交主檔	
			String sql="INSERT INTO chkinmain (subsysId,chkInStatusId,isDailyBuild,svnOLurl,svnOLver,creatorId,cDT,svnOriPath,apOriVer,svnFinalPath,apFinalVer) VALUES (?,?,?,?,?,?,?,?,?,?,?)";
			ps = conn.prepareStatement(sql, new String[]{"chkInMainId"});
			ps.setString(1, subsysId );
			ps.setInt(2, CheckInStatus.CHKIN2SVNing_SOURCE );	//繳交記錄狀態
			ps.setString(3, "t");				//是否為每日建置
			ps.setString(4, svnUrl );			
			ps.setLong(5, apVer );			
			ps.setString(6, empId );			//提交人員
			ps.setTimestamp(7, Widgets.cDT() );	//提交時間
			ps.setString(8, svnPath );
			ps.setLong(9, apVer );
			ps.setString(10, svnPath);
			ps.setLong(11, apVer );
			ps.execute();
			rs = ps.getGeneratedKeys();
			rs.next();
			final int CHECK_IN_ID = rs.getInt(1);
			if ( rs !=null ) {rs.close();}
			if ( ps !=null ) {ps.close();}
			
			ISVNEditor editor = null;
			SVNCommitInfo info = null;
			
			/*	此區開始建立SVN連線	*/
			
			
	        //String commitMessage = apRepo.info(svnOriPath, apOriVer).getCommitMessage();
	        String commitMessage = "";
	        editor = apRepo.getCommitEditor(null, new CommitMediator() );
            editor.openRoot(-1);
	        
        	//開始將檔案從 SVN AP 複制到 SVN 
        	manager = SVNClientManager.newInstance(new DefaultSVNOptions(), svnAP.get(1), svnAP.get(2)); // 如果需要用戶名密碼
			SVNLogClient logClient = manager.getLogClient();
			ISVNDirEntryHandler handler = new FileEntryHandler();
			
			fileList.clear();
			
			logClient.doList(SVNURL.parseURIEncoded(svnAP.get(0)+svnPath), SVNRevision.HEAD, SVNRevision.HEAD,false, true, handler); // 列出當前svn地址的目錄，對每個文件進行處理
			
			System.out.println("原始碼要來自："+svnAP.get(0) );
			System.out.println("原始碼要簽入："+svnCM_MR.get(0) );
			cmRepo = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(svnCM_MR.get(0)));
			authManager = SVNWCUtil.createDefaultAuthenticationManager(svnCM_MR.get(1), svnCM_MR.get(2));
	        cmRepo.setAuthenticationManager(authManager);
	        
	        if ( apRepo != null ) { apRepo.closeSession(); }
	        apRepo = SVNRepositoryFactory.create( SVNURL.parseURIEncoded(svnAP.get(0)+svnPath) );	//連線到SVN AP OL 的repo
	        authManager = SVNWCUtil.createDefaultAuthenticationManager(svnAP.get(1), svnAP.get(2));
	        apRepo.setAuthenticationManager(authManager);
	       
	        //我需要的COMMIT 還沒有簽進去
            editor = cmRepo.getCommitEditor(commitMessage, new CommitMediator() );
            editor.openRoot(-1);
            SVNProperties properties = new SVNProperties();
            
            for(int i=0; i<fileList.size();i++) {
            	I1S1 is = fileList.get(i);
            	String cmPath = "";
            	String[] pathArray = is.getS1().split("/");	//記錄這個檔案延途的路徑
            	int numberCloseDir = pathArray.length; //這是給SVN CM用的，算階層的(目前這行是先以Dir來看)，用以回到根節點
            	System.out.println("次數："+numberCloseDir);
            	if( is.getI1() == 0 ) {	//表示是資料夾
            		System.out.println("這個是資料夾："+is.getS1());
            		for(int j=0; j<numberCloseDir ;j++ ) {
            			cmPath += pathArray[j]+"/";	//這是給 SVN AP用的，抓資料的路徑
            			String path = cmPath.substring(0, cmPath.length()-1);
            			System.out.println("目前處理："+path);
            			try {
            				editor.addDir( path, null , -1 );
            	        	System.out.println("路徑："+path+"，不存在==>創建");
            	        } catch(Exception e) {
            	        	System.out.println("路徑："+path+"，存在");
            	        }
            		}
            		for(int j=0; j<numberCloseDir ;j++ ) {
            			editor.closeDir();
            		}
            	} else {	//表示是檔案
            		numberCloseDir--;
            		String fn = pathArray[numberCloseDir];
            		System.out.println("這個是檔案："+is.getS1()+",要close的次數"+numberCloseDir);
            		
            		//先用切換到它所該在的路徑下
            		for(int j=0; j<numberCloseDir ;j++ ) {
            			cmPath += pathArray[j]+"/";	//這是給 SVN AP用的，抓資料的路徑
            			System.out.println("目前處理："+pathArray[j]);
            			String path = cmPath.substring(0, cmPath.length()-1);
            			try {
            				editor.addDir( path, null , -1);
            	        	System.out.println("路徑："+pathArray[j]+"，不存在==>創建");
            	        } catch(SVNException e) {
            	        	System.out.println("路徑："+pathArray[j]+"，存在==>進入");
            	        }
            		}
            		cmPath += fn;
            		System.out.println("檔案名稱:"+fn+"\n");
            		
            		//重新宣告一個檔案，不然檔案會一直append下去
            		baos = new ByteArrayOutputStream();
            		apRepo.getFile( is.getS1(),apVer, properties, baos);
  			        ins  = new ByteArrayInputStream( baos.toByteArray() );
            		try {
            			editor.addFile( is.getS1(), null , -1 );
            			System.out.println("新增了檔案"+is.getS1());
        	        } catch(SVNException e) {
        	        	editor.openFile( is.getS1(), -1);
        	        	System.out.println("開啟了檔案"+is.getS1());
        	        }
            		editor.applyTextDelta( is.getS1(), null); 
            		String baseChecksum = deltaGenerator.sendDelta( is.getS1(), ins, editor, true);
    	    	    editor.textDeltaEnd( is.getS1() );
    	    	    editor.closeFile( is.getS1(), baseChecksum );
    	    	    
    	    	    for(int j=0; j<numberCloseDir ;j++ ) {
            			editor.closeDir();
            		}
            	}
            }
            
            info = editor.closeEdit();
	        //寫回 SVN CM的版次
    	    long cmOLrepoVer = info.getNewRevision();
    	    
    	    System.out.println("簽入結束，CM原始碼區的版次是："+cmOLrepoVer);
			Widgets.updateCHKINstatus(CHECK_IN_ID, CheckInStatus.READY_TRIGGER_JENKINS);
			
			System.out.println("簽入完成，準備驅動Jenkins，先更新各個job的內容");
			sql = "SELECT modId FROM mods WHERE subsysId=?";
			ps = conn.prepareStatement(sql);
			ps.setString(1, subsysId);
			rs = ps.executeQuery();
			while( rs.next() ) {
				String modId = rs.getString("modId");
				Widgets.updateJenkinsJobPostBuildURL(empId,modId, CHECK_IN_ID, subsysId, false);
				System.out.println("修改完了"+ modId );
			}
			
			Widgets.triggerJenkinsBuild( "BuildSaiFrom" );
			
			System.out.println("下次執行時間："+ctx.getNextFireTime());
    	    sql = "UPDATE chkinmain SET cmFileVer=? WHERE chkInMainId=?";
    	    ps = conn.prepareStatement(sql);
    	    ps.setLong(1, cmOLrepoVer);
			ps.setInt(2, CHECK_IN_ID);
			ps.execute();
			
			System.out.println("下次執行時間："+ctx.getNextFireTime());
		} catch(Exception e) {
			Widgets.errLog(e);
		} finally {
			try {
				if ( baos !=null ) {baos.close();}
				if ( ins !=null ) {ins.close();}
				if ( rs !=null ) {rs.close();}
				if ( ps !=null ) {ps.close();}
				if ( conn !=null ) {conn.close();}
				if ( cmRepo != null ) { cmRepo.closeSession(); }
				if ( apRepo != null ) { apRepo.closeSession(); }
				if ( manager != null ) { manager.dispose(); }	
			} catch (Exception e) { e.printStackTrace(); }
		}
	}
	
	class FileEntryHandler implements ISVNDirEntryHandler {
		public void handleDirEntry(SVNDirEntry entry) throws SVNException {
			I1S1 is = new I1S1();
			
			if ( entry.getKind() == SVNNodeKind.DIR ) {
				if ( !"".equalsIgnoreCase(entry.getRelativePath()) ) {
					is.setI1(0);
					is.setS1( entry.getRelativePath() );
					fileList.add( is );
				}
			} else if ( entry.getKind() == SVNNodeKind.FILE ) {
				is.setI1(1);
				is.setS1( entry.getRelativePath() );
				fileList.add( is );
			}
		}
	}
}
